#include "externtool.h"

#include <QProcess>
#include <QMessageBox>
#include <QFileDialog>

#include <stdlib.h>

#ifdef Q_OS_WIN
#include <windows.h>
#endif

#include <unistd.h>

#include "misc.h"
#include "mannasettings.h"

ExternTool::ExternTool(QWidget * parent, Qt::WindowFlags f)
    : QDialog(parent, f)
{
    setupUi(this);
}

void ExternTool::on_gccLocation_clicked()
{
    chooseLocation(gcc);
}

void ExternTool::on_fpcLocation_clicked()
{
    chooseLocation(fpc);
}

void ExternTool::chooseLocation(QLineEdit *dest) {
    QString s = QFileDialog::getExistingDirectory(
                    this,
                    tr("Please select the directory"),
                    QDir::cleanPath(dest->text()));

    if ( !s.isEmpty() ) {
        dest->setText( s );
        testExternTool();
    }
}

void ExternTool::on_test_clicked() {
    testExternTool();
}

bool ExternTool::testExternTool(bool silent)
{
    bool result = true;
    gccIcon->setPixmap( QPixmap(":/images/externtool/pass.png") );
    fpcIcon->setPixmap( QPixmap(":/images/externtool/pass.png") );

    QString suffix;
#ifdef Q_OS_WIN
    suffix = ".exe";
#endif

    if (!testGcc(silent, gcc->text())
            || !testGpp(silent, gcc->text())
            || !testGdb(silent, gcc->text())) {
        result = false;
        gccIcon->setPixmap( QPixmap(":/images/externtool/wrong.png") );
    }

    /* fpc test */
    if (!testFpc(silent, fpc->text())) {
        result = false;
        fpcIcon->setPixmap( QPixmap(":/images/externtool/wrong.png") );
    }

    return result;
}

bool ExternTool::checkExistence(QString name, bool silent, QString dir/* = QString() */)
{
    QString mingw, gcc, gpp, gdb, fpc, suffix;
    bool show;
    if (dir.isEmpty()) {
        MannaSettings::getMannaSettings()->
                compilerPath(mingw, fpc, suffix, show);
        gcc = mingw;
        gpp = mingw;
        gdb = mingw;
    } else {
        gcc = dir;
        gpp = dir;
        gdb = dir;
        fpc = dir;
    }
    QString target;
    if (name == "gcc")
        target = gcc;
    else if (name == "g++")
        target = gpp;
    else if (name == "gdb")
        target = gdb;
    else if (name == "fpc")
        target = fpc;
    else
        return false;
    QProcess *tester = new QProcess(this);
    target += "/" + name + suffix;
    tester->start(target, QStringList("-v"));
    tester->waitForFinished(5000);
    QString lu = tester->readAllStandardError(); //gcc use standard error to ouput version information
    lu += tester->readAllStandardOutput(); //fpc use standard ouput to output version information
    delete tester;
    QString sign = name;
    if (sign == "fpc")
        sign = "Free Pascal Compiler";
    else if (sign == "g++")
        sign = "gcc";
    else if (sign == "gdb")
        sign = "GNU gdb";
    if (!lu.contains(sign)) {
        if (!silent) {
            QMessageBox::warning(this, PROGRAM_NAME,
                        tr("Can not find ") + name + tr("!"),
                        QMessageBox::Ok);
        }
        return false;
    }
    return true;
}

bool ExternTool::testGcc(bool silent/* = false */, QString dir/* = QString() */)
{
    return checkExistence("gcc", silent, dir);
}

bool ExternTool::testGpp(bool silent/* = false */, QString dir/* = QString() */)
{
    return checkExistence("g++", silent, dir);
}

bool ExternTool::testGdb(bool silent/* = false */, QString dir/* = QString() */)
{
    return checkExistence("gdb", silent, dir);
}

bool ExternTool::testFpc(bool silent/* = false */, QString dir/* = QString() */)
{
    return checkExistence("fpc", silent, dir);
}

void ExternTool::on_buttonBox_clicked(QAbstractButton * button )
{
    // we only deal with "ok" and "cancel" clicks, all others are ignored
    if (buttonBox->button(QDialogButtonBox::Cancel) ==  button) {
        reject();
        return;
    } else if (buttonBox->button(QDialogButtonBox::Ok) !=  button) {
        return;
    }
    setPathToEnvironment();
    writeSettings();
    accept();
    return;
}

void ExternTool::setDefaultPath()
{
    QString mingw, fpcPath, suffix;
    bool show;
    MannaSettings::getMannaSettings()->compilerPath(mingw, fpcPath, suffix, show);
    gcc->setText(QDir::toNativeSeparators(mingw));
    fpc->setText(QDir::toNativeSeparators(fpcPath));
    checkBox->setChecked(show);
}

void ExternTool::checkExternTool()
{
    ExternTool checker;
    if (checker.testExternTool(true)) {
        return;
    }
    checker.setDefaultPath();
    if (checker.testExternTool(true)) {
        checker.setPathToEnvironment();
        checker.writeSettings();
        return;
    }

    checker.exec();
}

void ExternTool::resetExternTool()
{
    ExternTool checker;
    checker.setDefaultPath();
    checker.testExternTool(true);
    checker.exec();
}

void ExternTool::setPathToEnvironment()
{
    QStringList sysenv = QProcess::systemEnvironment();
    QString pathenv;
    foreach( QString env, sysenv ) {
        if (env.section('=', 0, 0).toLower() == "path") {
            pathenv = env.section('=', 1);
            break;
        }
    }
#ifdef Q_OS_WIN
    pathenv = pathenv + ";" + gcc->text() + ";" + fpc->text();
    SetEnvironmentVariable ((wchar_t*)L"PATH", (wchar_t*)pathenv.utf16());
#else
    pathenv = pathenv + ":" + gcc->text() + ":" + fpc->text();
    setenv ("PATH", (const char *)pathenv.toLocal8Bit(), 1);
#endif
}

void ExternTool::writeSettings()
{
    MannaSettings *settings = MannaSettings::getMannaSettings();
    settings->beginGroup("ExternTools");
    settings->setValue("gcc_path", QDir::fromNativeSeparators(gcc->text()));
    settings->setValue("fpc_path", QDir::fromNativeSeparators(fpc->text()));
    (checkBox->isChecked())?settings->setValue("show_flag", "true"):settings->setValue("show_flag", "false");
    settings->endGroup();
}

void ExternTool::setEnvironment() {
    ExternTool checker;
    checker.setDefaultPath();
    checker.setPathToEnvironment();
    checker.writeSettings();
    return;
}
